home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
oper_sys
/
weyl
/
weyl_lsp.lha
/
domain-support.lisp
< prev
next >
Wrap
Text File
|
1991-10-02
|
11KB
|
318 lines
;;; -*- Mode:Lisp; Package:Weyli; Base:10; Lowercase:T; Syntax:Common-Lisp -*-
;;; ===========================================================================
;;; Domains
;;; ===========================================================================
;;; (c) Copyright 1989, 1991 Cornell University
;;; $Id: domain-support.lisp,v 2.13 1991/10/02 17:46:30 rz Exp $
(in-package "WEYLI")
(defclass domain ()
((operation-table :initform (make-hash-table))))
(defmethod print-object ((d domain) stream)
(format stream "#<Domain: ~A>" (class-name (class-of d))))
;; This is so that you can pretty print objects in lucid. It appears,
;; that you are not supposed to use PRINC inside these methods.
#+Lucid
(defmethod print-object :around ((object domain) stream)
(let ((*print-pretty* nil))
(call-next-method object stream)))
(defmacro define-operations (domain &body operations)
`(defmethod parse-operations :after ((d ,domain))
(parse-operation-list d ',operations)))
(defmethod parse-operation-list ((d domain) operation-list)
(with-slots (operation-table) d
(loop for ((operation . arguments) nil values) on operation-list by #'cdddr
do (setf (gethash operation operation-table)
(list operation arguments values)))))
;; Need a dummy primary method to hang all the :after methods on.
(defmethod parse-operations ((d domain))
nil)
(defmethod initialize-instance :after ((d domain) &rest plist)
(declare (ignore plist))
(parse-operations d))
(defmethod list-operations ((d domain))
(with-slots (operation-table) d
(let (ops)
(maphash (lambda (key value)
(declare (ignore value))
(push key ops))
operation-table)
ops)))
(defmethod operation-arguments ((d domain) operation)
(with-slots (operation-table) d
(subst (class-name (class-of d)) 'self
(second (gethash operation operation-table)))))
(defmethod operation-values ((d domain) operation)
(with-slots (operation-table) d
(subst (class-name (class-of d)) 'self
(third (gethash operation operation-table)))))
#+Genera
(defmethod describe-operations ((d domain) &optional no-complaints)
(declare (ignore no-complaints))
(let* ((class-name (class-name (class-of d)))
(domain-element (cond ((null (rest (get class-name 'domain-elements)))
(first (get class-name 'domain-elements)))
(t (format nil "~A element" class-name)))))
(labels ((canonicalize-class (name)
(cond ((eql name 'self) class-name)
((atom name) name)
((equal name '(element self))
domain-element)
(t (mapcar #'canonicalize-class name)))))
(format t "~&~S is a ~A~%" d class-name)
(fresh-line)
(with-slots (operation-table) d
(scl:formatting-table ()
(scl:with-character-style ('(nil :italic nil))
(scl:formatting-row ()
(scl:formatting-cell ()
(princ "Operation"))
(scl:formatting-cell ()
(princ "Arguments"))
(scl:formatting-cell ()
(princ "Values"))))
(maphash (lambda (key value)
(declare (ignore key))
(scl:formatting-row ()
(scl:formatting-cell ()
(princ (first value)))
(scl:formatting-cell ()
(format t "~A~{, ~A~}"
(canonicalize-class (first (second value)))
(mapcar #'canonicalize-class
(rest (second value)))))
(scl:formatting-cell ()
(princ (canonicalize-class (third value))))))
operation-table))))))
#-Genera
(defmethod describe-operations ((d domain) &optional no-complaints)
(declare (ignore no-complaints))
(let* ((class-name (class-name (class-of d)))
(element-classes (get class-name 'element-classes))
(domain-element (cond ((and element-classes
(null (rest element-classes)))
(first element-classes))
(t (format nil "~A element" class-name)))))
(labels ((canonicalize-class (name)
(cond ((eql name 'self) class-name)
((atom name) name)
((equal name '(element self))
domain-element)
(t (mapcar #'canonicalize-class name)))))
(format t "~&~S is a ~A~%" d class-name)
(fresh-line)
(with-slots (operation-table) d
(format t "Operation Arguments Values")
(maphash (lambda (key value)
(declare (ignore key))
(format t "~&(~A ~A~{, ~A~}) -> ~A~%"
(first value)
(canonicalize-class (first (second value)))
(mapcar #'canonicalize-class
(rest (second value)))
(canonicalize-class (third value))))
operation-table)))))
(defmethod required-operations ((d domain) &optional fun)
(let* ((class-name (class-name (class-of d)))
(element-classes (get class-name 'element-classes))
(domain-element (cond ((and element-classes
(null (rest element-classes)))
(first element-classes))
(t (cons 'or element-classes))))
list)
(labels ((canonicalize-class (name)
(cond ((eql name 'self) class-name)
((atom name) name)
((equal name '(element self))
domain-element)
(t (mapcar #'canonicalize-class name)))))
(unless fun
(setq fun (lambda (form)
(push (cons (first form)
(mapcar #'canonicalize-class (second form)))
list))))
(with-slots (operation-table) d
(maphash (lambda (key value)
(declare (ignore key))
(%funcall fun value))
operation-table))
list)))
#+PCL
(defmethod check-domain ((d domain))
(required-operations d
(lambda (form)
(let ((operation (first form))
(args (rest form)))
(map-over-arglist-combinations (class-name (class-of d)) args
(lambda (arg-names)
(let ((args (loop for type in arg-names
collect (find-class type nil))))
(loop for method in (pcl::generic-function-methods
(symbol-function operation))
do (when (equal args
(pcl::method-type-specifiers method))
(return t))
finally (format t "No method for ~S~%"
(cons operation arg-names))))))))))
(defun map-over-arglist-combinations (self arglist fun)
(labels ((recur (arglist types)
(cond ((null arglist)
(%funcall fun (reverse types)))
((atom (first arglist))
(recur (rest arglist) (cons (first arglist) types)))
((eql (first (first arglist)) 'or)
(loop for type in (rest (first arglist))
do (recur (cons type (rest arglist)) types)))
((eql (first (first arglist)) 'element)
(loop for type in (get self 'element-classes)
do (recur (cons type (rest arglist)) types)))
(t (error "Don't understand arglist entry: ~S"
(first arglist))))))
(recur (first arglist) ())))
;; Domain creators
(defvar *domains* ()
"List of domains currently in use")
(defvar *morphisms* ()
"A list of the morphisms currently in use.")
(defvar *lisp-numbers* ()
"The (unique) domain for lisp numbers")
(defun reset-domains ()
(unless *lisp-numbers*
(setq *lisp-numbers* (make-instance 'lisp-numbers)))
(setq *domains* (list *lisp-numbers*))
(setq *morphisms* nil))
(defmacro add-domain (predicate &body body)
`(add-domain-internal ,predicate (lambda () ,@body)))
(defun add-domain-internal (predicate body)
(let ((domain (find nil *domains* :test (lambda (a b)
(declare (ignore a))
(%funcall predicate b)))))
(when (null domain)
(setq domain (%funcall body))
(push domain *domains*))
domain))
(defun false (&rest args)
(declare (ignore args))
nil)
(defun true (&rest args)
(declare (ignore args))
t)
;; Use this macro to define domain creators.
(defmacro define-domain-creator (name args creator &key predicate body)
(labels ((parse-args (args)
(cond ((null args)
args)
((member (first args) '(&optional &key))
(parse-args (rest args)))
((eql (first args) '&rest)
(error "Can't handle &rest args here"))
((atom (first args))
(cons (first args) (parse-args (rest args))))
(t (cons (first (first args))
(parse-args (rest args)))))))
(let ((internal-fun (intern (format nil "MAKE-~A*" name)))
(true-args (parse-args args)))
`(progn
(defmethod ,internal-fun ,args ,creator)
(defmethod ,(intern (format nil "MAKE-~A" name)) ,args
(add-domain #'false (,internal-fun ,@true-args)))
,@(when predicate
`((defmethod ,(intern (format nil "GET-~A" name)) ,args
(add-domain #',predicate (,internal-fun ,@true-args)))))
,@(when body
`((defmethod ,(intern (format nil "GET-~A" name)) ,args
,body)))))))
(defmacro with-new-weyl-context ((plist) &body body)
`(let ((*domains* nil)
(*morphisms* nil)
(*allow-coercions*
,(or (getf plist :allow-coercions) '*allow-coercions*)))
,@body))
;; All elements of a domain should include this class
(defclass domain-element ()
((domain :initarg :domain
:reader domain-element-domain)))
(defmacro define-domain-element-classes (domain &body element-classes)
`(progn
,@(loop for element-class in element-classes
collect
`(cond ((eql (get ',element-class 'domain-class) ',domain))
(t
(when (get ',element-class 'domain-class)
(format t "WARNING: Reset domain-class of ~S~%"
',element-class))
(setf (get ',element-class 'domain-class) ',domain))))
(setf (get ',domain 'element-classes) ',element-classes)))
(defmethod domain-element-classes ((domain domain))
(get (class-name (class-of domain)) 'element-classes))
;; This is so that you can pretty print objects in lucid. It appears,
;; that you are not supposed to use PRINC inside these methods.
#+Lucid
(defmethod print-object :around ((object domain-element) stream)
(let ((*print-pretty* nil))
(call-next-method object stream)))
(defmethod domain-of ((element domain-element))
(domain-element-domain element))
(defgeneric coerce (elt domain))
(defgeneric coercible? (elt domain))
(defmacro defmethod-binary (op domain (x y) &body body)
`(defmethod ,op ((,x ,domain) (,y ,domain))
(let ((domain (domain-of ,x)))
(cond ((eql domain (domain-of ,y))
,@body)
(t (error "Binary operation of two elements of different~
domains: (~S, ~S)"
,x ,y))))))
;; These are often of use when defining generic operations for domains.
(defvar *domain* ()
"Within the context of an operation, the current domain")
(defmacro bind-domain-context (domain &body body)
`(%bind-dynamic-domain-context ,domain
(lambda ()
#+Genera (declare (sys:downward-function))
,@body)))
(defmethod %bind-dynamic-domain-context ((domain domain) function)
(let ((*domain* domain))
(%funcall function)))